package kuzmenkov.applet;

import java.awt.Color;
import java.io.IOException;
import java.io.Reader;
import java.util.Enumeration;
import java.util.logging.Logger;

import nanoxml.XMLElement;
import nanoxml.XMLParseException;

/**
 * This class convert a screen from a XML description to an instance of a Screen
 * @author Michael Kuzmenkov
 *
 */
public class XMLScreenConverter {
	
	private Logger log = Logger.getLogger(this.getClass().getPackage()
			.getName());
		
	/**
	 * Loads the screen from the XML screen file
	 * @param xmlScreenReader the reader of the file
	 * @return the screen
	 * @throws IOException 
	 * @throws XMLParseException 
	 * @throws Exception
	 */
	public Screen load (Reader xmlScreenReader) throws XMLParseException, IOException  {
		if (xmlScreenReader==null)
			throw new IllegalArgumentException("The argument xmlScreenReader is null");
		XMLElement xmlImage = new XMLElement();
		xmlImage.parseFromReader(xmlScreenReader);
		Screen screen = convert(xmlImage);
		assert (screen!=null);
		return screen;
	}
	
	/**
	 * Converts the screen from the XML description to the instance of Screen 
	 * @param xmlScreen the XML description
	 * @return the screen
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public Screen convert (XMLElement xmlScreen) {
		if (xmlScreen == null )
			throw new IllegalArgumentException("The argument xmlScreen is null");
		if (!xmlScreen.getName().equalsIgnoreCase(Screen.ROOT_NODE))
			throw new IllegalArgumentException("The XML document is not a screen");
		
		Screen screen  = createXMLScreen(xmlScreen);
		assert (screen != null);
		
		Field[] fields = null;
		Enumeration<XMLElement> children = xmlScreen.enumerateChildren();;
		assert (children!=null);
		assert (children.hasMoreElements());
		while (children.hasMoreElements()) {
			XMLElement child = children.nextElement();
			
			String name = child.getName();
			log.fine(name);
			if (Screen.FIELDS_NODE.equalsIgnoreCase(name)){
				if (child.countChildren()!=screen.getFieldCount())
					throw new IllegalStateException("The quantity of the field nodes doesn't equal the attribute fieldCount");
				fields = analyseFieldsNode (child);
				break;
			}
		}

		screen.setFields(fields);
		
		return screen;
	}

	/**
	 * Returns a Color based on 'colorName' which must be one of the predefined colors in
	 * java.awt.Color. Returns null if colorName is not valid.
	 * @param colorName the name of the color
	 * @return the color
	 */
	public Color getColor(String colorName) {
    	if (colorName==null || colorName.isEmpty()){
    		assert (false);
    		return null;
    	} else 
    		colorName.toUpperCase();
        try {
            java.lang.reflect.Field field = Class.forName(Color.class.getName()).getField(colorName);
            return (Color)field.get(null);
        } catch (Exception e) {
        	log.warning("The color '"+colorName+"' wasn't found");
            return null;
        }
    }

	
	@SuppressWarnings("unchecked")
	private Field[] analyseFieldsNode(XMLElement fieldsNode) {
		assert (fieldsNode!=null);
		
		Enumeration<XMLElement> fieldNodes = fieldsNode.enumerateChildren();
		assert (fieldNodes != null);
		assert (fieldNodes.hasMoreElements());
		
		int size = fieldsNode.countChildren();
		Field[] fields = new Field[size];
		
		int index = 0;
		while (fieldNodes.hasMoreElements()) {
			XMLElement fieldNode = fieldNodes.nextElement();
			assert (fieldNode != null);
			if (!Screen.FIELD_NODE.equalsIgnoreCase(fieldNode.getName())){
				log.warning("The fields node consist a "+fieldNode.getName()+" node that isn't a field node");
				continue;
			}
			Field field = createField (fieldNode);
			
			assert (field!=null);
			assert (index >=0);
			assert (index < size);
			fields[index]= field;
			++index;
		}
		
		assert (fields.length == fieldsNode.countChildren());
		return fields;
	}

	/**
	 * Creates the instance of a Field from the field node
	 * @param fieldNode the node
	 * @return the instance
	 */
	private Field createField(XMLElement fieldNode) {
		assert (fieldNode!=null);
		assert (Screen.FIELD_NODE.equalsIgnoreCase(fieldNode.getName()) );
		
		String colorName = fieldNode.getStringAttribute(Screen.COLOR_ATT_OF_FIELD);
		Color color = (colorName!=null&&!colorName.isEmpty()) ? getColor(colorName) :null;
		log.fine("colorName = "+colorName+", color = "+color);

		int column = fieldNode.getIntAttribute(Screen.COLUMN_ATT_OF_FIELD);
		int length = fieldNode.getIntAttribute(Screen.LENGTH_ATT_OF_FIELD);
		int row = fieldNode.getIntAttribute(Screen.ROW_ATT_OF_FIELD);
		
		
		
		String content = fieldNode.getContent();
		log.finer("content = '"+content+"'");
		if (content ==null || content.isEmpty())
			throw new IllegalStateException("The content of the node '"+fieldNode.getName()+"' is null of empty");
		
		if (length!=content.length())
			log.warning("The attribut 'length' doesn't equal the length of the content '"+content+"': "+
					length +" != "+content.length());
		
		Field field = new Field(color, column, row, content);
		return field;
	}

	private Screen createXMLScreen (XMLElement xmlScreen) {
		int columns = xmlScreen.getIntAttribute(Screen.COLUMNS_ATT_OF_SCREEN);
		int rows = xmlScreen.getIntAttribute(Screen.ROWS_ATT_OF_SCREEN);
		int fieldCount = xmlScreen.getIntAttribute(Screen.FIELD_COUNT_ATT_OF_SCREEN);
		String name = xmlScreen.getStringAttribute(Screen.NAME_ATT_OF_SCREEN);
				
		Screen screen = new Screen(name, columns, rows, fieldCount);
		return screen;
	}
	
}
